package net.NingXiaRuiTai.ShortChain.service.impl;


import com.baomidou.mybatisplus.core.toolkit.Assert;
import lombok.extern.slf4j.Slf4j;
import net.NingXiaRuiTai.ShortChain.Manager.DomainManager;
import net.NingXiaRuiTai.ShortChain.Manager.GroupCodeMappingManager;
import net.NingXiaRuiTai.ShortChain.Manager.LinkGroupManager;
import net.NingXiaRuiTai.ShortChain.Manager.ShortLinkManager;
import net.NingXiaRuiTai.ShortChain.component.ShortLinkComponent;
import net.NingXiaRuiTai.ShortChain.config.RabbitMQConfig;
import net.NingXiaRuiTai.ShortChain.controller.request.ShortLinkAddRequest;
import net.NingXiaRuiTai.ShortChain.controller.request.ShortLinkDelRequest;
import net.NingXiaRuiTai.ShortChain.controller.request.ShortLinkPageRequest;
import net.NingXiaRuiTai.ShortChain.controller.request.ShortLinkUpdateRequest;
import net.NingXiaRuiTai.ShortChain.enums.DomainTypeEnum;
import net.NingXiaRuiTai.ShortChain.enums.EventMessageType;
import net.NingXiaRuiTai.ShortChain.enums.ShortLinkStateEnum;
import net.NingXiaRuiTai.ShortChain.interceptor.LoginInterceptor;
import net.NingXiaRuiTai.ShortChain.model.*;
import net.NingXiaRuiTai.ShortChain.mapper.ShortLinkMapper;
import net.NingXiaRuiTai.ShortChain.model.VO.ShortLinkVO;
import net.NingXiaRuiTai.ShortChain.service.ShortLinkService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.NingXiaRuiTai.ShortChain.utils.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Yuan
 * @since 2025-02-16
 */
@Service
@Slf4j
public class ShortLinkServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLinkDO> implements ShortLinkService {
    @Resource
    private ShortLinkManager shortLinkManager;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private RabbitMQConfig rabbitMQConfig;
    @Resource
    private DomainManager domainManager;
    @Resource
    private LinkGroupManager linkGroupManager;
    @Resource
    private ShortLinkComponent shortLinkComponent;

    @Resource
    GroupCodeMappingManager groupCodeMappingManager;
    @Resource
    RedisTemplate<Object, Object> redisTemplate;


    @Override
    public ShortLinkVO parseShortLinkCode(String shortLinkCode) {
        ShortLinkDO byShortLinkCode = shortLinkManager.findByShortLinkCode(shortLinkCode);
        if (byShortLinkCode == null) {
            return null;
        }
        ShortLinkVO shortLinkVO = new ShortLinkVO();
        BeanUtils.copyProperties(byShortLinkCode, shortLinkVO);

        return shortLinkVO;
    }

    @Override
    public JsonData creatShortLink(ShortLinkAddRequest addRequest) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        String s = CommonUtil.addUrlPrefix(addRequest.getOriginalUrl());
        addRequest.setOriginalUrl(s);
        EventMessage eventMessage = EventMessage.builder()
                .accountNo(accountNo)
                .content(JsonUtil.obj2Json(addRequest))
                .messageId(IdUtil.generateId().toString())
                .eventMessageType(EventMessageType.SHORT_LINK_ADD.name())
                .build();
        rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkEventExchange(), rabbitMQConfig.getShortLinkAddRoutingKey(), eventMessage);

        return JsonData.buildSuccess();
    }

    /**
     * 处理短链新增逻辑
     * <p>
     * //判断短链域名是否合法
     * //判断组名是否合法
     * //生成长链摘要
     * //生成短链码
     * //加锁
     * //查询短链码是否存在
     * //构建短链对象
     * //保存数据库
     *
     * @param eventMessage
     * @return
     */
    @Override
    public boolean handAddShortLink(EventMessage eventMessage) {
        Long accountNo = eventMessage.getAccountNo();
        String eventMessageType = eventMessage.getEventMessageType();
        ShortLinkAddRequest addRequest = JsonUtil.json2Obj(eventMessage.getContent(), ShortLinkAddRequest.class);
        //判断短链域名是否合法
        DomainDO domainDO = checkDomain(addRequest.getDomainType(), addRequest.getDomainId(), accountNo);

        //校验组是否合法
        LinkGroupDO linkGroupDO = checkLinkGroup(addRequest.getGroupId(), accountNo);

        //长链摘要
        String originalUrlDigest = CommonUtil.MD5(addRequest.getOriginalUrl());

        //短链码重复标记
        boolean shortLinkCodeRepeat = false;
        //生成短链码
        String shortLinkCode = shortLinkComponent.createShortLink(addRequest.getOriginalUrl());

        //加锁
        //key1是短链码，ARGV[1]是accountNo,ARGV[2]是过期时间
        String script = "if redis.call('EXISTS',KEYS[1])==0 then redis.call('set',KEYS[1],ARGV[1]); " +
                "redis.call('expire',KEYS[1],ARGV[2]); return 1;" +
                " elseif redis.call('get',KEYS[1]) == ARGV[1] then return 2;" +
                " else return 0; end;";
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(shortLinkCode), accountNo.toString(), "100");
        if (result > 0) {
            //加锁成功


            //C端处理
            if (EventMessageType.SHORT_LINK_ADD_LINK.name().equalsIgnoreCase(eventMessageType)) {
                //先判断是否短链码被占用
                ShortLinkDO ShortLinCodeDOInDB = shortLinkManager.findByShortLinkCode(shortLinkCode);
                if (ShortLinCodeDOInDB == null) {
                    ShortLinkDO shortLinkDO = ShortLinkDO.builder()
                            .accountNo(accountNo)
                            .code(shortLinkCode)
                            .title(addRequest.getTitle())
                            .originalUrl(addRequest.getOriginalUrl())
                            .domain(domainDO.getValue())
                            .groupId(linkGroupDO.getId())
                            .expired(addRequest.getExpired())
                            .sign(originalUrlDigest)
                            .state(ShortLinkStateEnum.ACTIVE.name())
                            .del(0)
                            .build();
                    shortLinkManager.addShortLink(shortLinkDO);
                    return true;
                } else {
                    //短链原始版本号自增
                    log.error("C端短链码重复：{}", eventMessage);
                    shortLinkCodeRepeat = true;
                }

            } else if (EventMessageType.SHORT_LINK_ADD_MAPPING.name().equalsIgnoreCase(eventMessageType)) {
                GroupCodeMappingDO groupCodeMappingDOInDB = groupCodeMappingManager.findByCodeAndGroupId(shortLinkCode, accountNo, linkGroupDO.getId());
                //B端处理
                if (groupCodeMappingDOInDB == null) {
                    GroupCodeMappingDO groupCodeMappingDO = GroupCodeMappingDO.builder()
                            .accountNo(accountNo)
                            .code(shortLinkCode)
                            .title(addRequest.getTitle())
                            .originalUrl(addRequest.getOriginalUrl())
                            .domain(domainDO.getValue())
                            .groupId(linkGroupDO.getId())
                            .expired(addRequest.getExpired())
                            .sign(originalUrlDigest)
                            .state(ShortLinkStateEnum.ACTIVE.name())
                            .del(0)
                            .build();

                    groupCodeMappingManager.add(groupCodeMappingDO);
                    return true;
                } else {
                    log.error("B端短链码重复：{}", eventMessage);
                    shortLinkCodeRepeat = true;

                }


            }
        } else {
            //加锁失败自旋处理
            log.error("加锁失败！", eventMessage);
            //睡眠100ms
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
            }
            shortLinkCodeRepeat = true;


        }
        if (shortLinkCodeRepeat) {
            String newUrl = CommonUtil.addUrlPrefixVersion(addRequest.getOriginalUrl());

            addRequest.setOriginalUrl(newUrl);
            eventMessage.setContent(JsonUtil.obj2Json(addRequest));
            log.warn("短链码重新生成：{}", eventMessage);
            handAddShortLink(eventMessage);

        }
        return false;
    }

    @Override
    public Map<String, Object> pageByGroupId(ShortLinkPageRequest request) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        Map<String, Object> map = groupCodeMappingManager.
                pageShortLinkByGroupId(request.getPage(), request.getSize(), accountNo, request.getGroupId());
        return map;

    }

    @Override
    public JsonData delShortLink(ShortLinkDelRequest delRequest) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

        EventMessage eventMessage = EventMessage.builder()
                .accountNo(accountNo)
                .content(JsonUtil.obj2Json(delRequest))
                .messageId(IdUtil.generateId().toString())
                .eventMessageType(EventMessageType.SHORT_LINK_DEL.name())
                .build();
        rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkEventExchange(), rabbitMQConfig.getShortLinkDelRoutingKey(), eventMessage);

        return JsonData.buildSuccess();
    }

    @Override
    public JsonData updateShortLink(ShortLinkUpdateRequest updateRequest) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

        EventMessage eventMessage = EventMessage.builder()
                .accountNo(accountNo)
                .content(JsonUtil.obj2Json(updateRequest))
                .messageId(IdUtil.generateId().toString())
                .eventMessageType(EventMessageType.SHORT_LINK_UPDATE.name())
                .build();
        rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkEventExchange(), rabbitMQConfig.getShortLinkUpdateRoutingKey(), eventMessage);

        return JsonData.buildSuccess();
    }

    @Override
    public boolean handDelShortLink(EventMessage eventMessage) {

        Long accountNo = eventMessage.getAccountNo();
        String eventMessageType = eventMessage.getEventMessageType();
        ShortLinkDelRequest delRequest = JsonUtil.json2Obj(eventMessage.getContent(), ShortLinkDelRequest.class);


        if (EventMessageType.SHORT_LINK_DEL_LINK.name().equalsIgnoreCase(eventMessageType)) {
            ShortLinkDO shortLinkDO = ShortLinkDO.builder()
                    .code(delRequest.getCode())
                    .accountNo(accountNo)
                    .del(1)
                    .build();
            int row = shortLinkManager.delShortLink(shortLinkDO);
            log.debug("删除C端短链：{}", row);
            return true;
        } else if (EventMessageType.SHORT_LINK_DEL_MAPPING.name().equalsIgnoreCase(eventMessageType)) {

            GroupCodeMappingDO groupCodeMappingDO = GroupCodeMappingDO.builder()
                    .id(delRequest.getGroupCodeMappingId())
                    .accountNo(accountNo)
                    .code(delRequest.getCode())
                    .groupId(delRequest.getGroupId())
                    .build();

            int row = groupCodeMappingManager.delShortLink(groupCodeMappingDO);
            log.debug("删除B端短链:{}", row);
            return true;
        }


        return false;


    }

    @Override
    public boolean handUpdateShortLink(EventMessage eventMessage) {
        Long accountNo = eventMessage.getAccountNo();
        String messageType = eventMessage.getEventMessageType();

        ShortLinkUpdateRequest request = JsonUtil.json2Obj(eventMessage.getContent(), ShortLinkUpdateRequest.class);

        //校验短链域名
        DomainDO domainDO = checkDomain(request.getDomainType(), request.getDomainId(), accountNo);

        //C端处理
        if (EventMessageType.SHORT_LINK_UPDATE_LINK.name().equalsIgnoreCase(messageType)) {

            ShortLinkDO shortLinkDO = ShortLinkDO.builder()
                    .code(request.getCode())
                    .title(request.getTitle())
                    .domain(domainDO.getValue())
                    .accountNo(accountNo).build();

            int rows = shortLinkManager.updateShortLink(shortLinkDO);
            log.debug("更新C端短链，rows={}", rows);
            return true;

        } else if (EventMessageType.SHORT_LINK_UPDATE_MAPPING.name().equalsIgnoreCase(messageType)) {
            //B端处理
            GroupCodeMappingDO groupCodeMappingDO = GroupCodeMappingDO
                    .builder()
                    .code(request.getCode())
                    .groupId(request.getGroupCodeMappingId())
                    .accountNo(accountNo)
                    .title(request.getTitle())
                    .domain(domainDO.getValue())
                    .build();

            int rows = groupCodeMappingManager.updateShortLink(groupCodeMappingDO);
            log.debug("更新B端短链，rows={}", rows);
            return true;
        }
        return false;

    }


    private DomainDO checkDomain(String domainType, Long domainId, Long accountNo) {
        DomainDO domainDO;

        if (domainType.equalsIgnoreCase(DomainTypeEnum.CUSTOM.name())) {
            domainDO = domainManager.findById(accountNo, domainId);

        } else {
            domainDO = domainManager.findByDomainTypeAndID(domainId, DomainTypeEnum.OFFICIAL);
        }
        Assert.notNull(domainDO, "短链域名不合法");
        return domainDO;
    }

    /**
     * 校验组名
     *
     * @param groupId
     * @param accountNo
     * @return
     */
    private LinkGroupDO checkLinkGroup(Long groupId, Long accountNo) {


        LinkGroupDO linkGroupDO = linkGroupManager.detail(groupId, accountNo);
        Assert.notNull(linkGroupDO, "组名不合法");
        return linkGroupDO;
    }

}
